package com.ams.studio.xml;

import com.ams.studio.xml.annotation.Contract;
import com.ams.studio.xml.annotation.ContractNode;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**

 * Created by zhongyl on 2015/12/10.

 */
public class ObjectInfoCache {

    private static volatile Object syncLock = new Object();
    private static volatile Map<Class<?>, List<ObjectInfo>> cache = new HashMap<Class<?>, List<ObjectInfo>>();

    public static List<ObjectInfo> getObjectInfo(Class<?> clazz) {
        synchronized (cache) {
            List<ObjectInfo> value = cache.get(clazz);
            if (value != null || cache.containsKey(clazz)) {
                return value;
            }
        }

        synchronized (syncLock) {
            List<ObjectInfo> list = buildObjectInfo(clazz);
            synchronized (cache) {
                if (!cache.containsKey(clazz)) {
                    cache.put(clazz, list);
                    return list;
                } else {
                    return cache.get(clazz);
                }
            }
        }
    }

    private static List<ObjectInfo> buildObjectInfo(Class<?> clazz) {
        Contract annotation = clazz.getAnnotation(Contract.class);
        if (annotation == null) {
            return null;
        }

        List<ObjectInfo> container = new ArrayList<ObjectInfo>();
        annotation = clazz.getSuperclass().getAnnotation(Contract.class);
        if (annotation != null) {
            buildObjectInfoRecursive(clazz.getSuperclass(), container);
        }

        List<Field> fields = extractBusinessNodes(clazz);
        if (fields != null) {
            container.add(ObjectInfo.New(clazz, fields));
        }

        return container;
    }

    private static void buildObjectInfoRecursive(Class<?> clazz, List<ObjectInfo> container) {
        Contract annotation = clazz.getSuperclass().getAnnotation(Contract.class);
        if (annotation != null) {
            buildObjectInfoRecursive(clazz.getSuperclass(), container);
        }

        List<Field> fields = extractBusinessNodes(clazz);
        if (fields != null) {
            container.add(ObjectInfo.New(clazz, fields));
        }
    }

    private static List<Field> extractBusinessNodes(Class<?> clazz) {
        Contract contract = clazz.getAnnotation(Contract.class);
        if (contract == null) {
            return null;
        }

        Field[] fields = clazz.getDeclaredFields();
        if (fields == null || fields.length == 0) {
            return null;
        }

        ArrayList<Field> bizNodeFields = new ArrayList<Field>();
        for (Field f : fields) {
            ContractNode annotation = f.getAnnotation(ContractNode.class);
            if (annotation != null) {
                bizNodeFields.add(f);
            }
        }

        return bizNodeFields;
    }
}
